package me.keiwu.pub.util.redis;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;

import java.util.Collections;

/**
 * Created by kei on 09/11/2017.
 */
@Component
public class RedisLock {

    private static Logger logger = LoggerFactory.getLogger(RedisLock.class);

    private static final String LOCK_SUCCESS = "OK";
    private static final Long RELEASE_SUCCESS = 1L;
    private static final Long EXPIRE_LOCK_MIL_SECONDS = 10000L; // 毫秒

    // NX|XX, NX -- Only set the key if it does not already exist. XX -- Only set the key if it already exist.
    private static final String SET_IF_NOT_EXIST = "NX";
    // EX|PX, expire time units: EX = seconds; PX = milliseconds
    private static final String SET_WITH_EXPIRE_TIME = "PX";


    @Autowired
    private ShardedJedisPool shardedJedisPool;


    /**
     * 获取锁。。因为腾讯云redis不支持执行lua脚本，所以折衷实现
     * @param lockKey
     * @param requestId
     * @return
     */
    public boolean tryLock(String lockKey, String requestId) {
        ShardedJedis jedis = null;
        try {
            jedis = shardedJedisPool.getResource();
            String current = "" + System.currentTimeMillis();
            if (jedis.setnx(lockKey, current) > 0) {
                return true;
            } else {
                Long setTime = Long.valueOf(jedis.get(lockKey));
                // 过长时间不解锁，作为遗留的锁，自动销毁
                if (System.currentTimeMillis() - setTime > EXPIRE_LOCK_MIL_SECONDS) {
                    return jedis.del(lockKey) > 0;
                }
            }
            return false;
        } catch (Exception e) {
            logger.error("exception in tryLock: ", e);
            return false;
        } finally {
            if (jedis != null) jedis.close();
        }
    }


    /**
     * 释放锁，因为腾讯云redis不支持执行lua脚本，所以折衷实现
     * @param lockKey
     * @param requestId
     * @return
     */
    public boolean release(String lockKey, String requestId) {
        ShardedJedis jedis = null;
        try {
            jedis = shardedJedisPool.getResource();
            return jedis.del(lockKey) > 0;
        } catch (Exception e) {
            logger.error("exception in release: ", e);
            return false;
        } finally {
            if (jedis != null) jedis.close();
        }
    }










    @Deprecated
    public boolean tryLockOld(String lockKey, String requestId) {
        return this.tryLockOld(lockKey, requestId, EXPIRE_LOCK_MIL_SECONDS);
    }
    @Deprecated
    public boolean tryLockOld(String lockKey, String requestId, long expireMsecs) {
        ShardedJedis jedis = null;
        try {
            jedis = shardedJedisPool.getResource();
            long expire = System.currentTimeMillis() + expireMsecs + 1;
            String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expire);
            if (LOCK_SUCCESS.equals(result)) {
                return true;
            }
            return false;
        } catch (Exception e) {
            logger.error("exception in tryLock: ", e);
            return false;
        } finally {
            if (jedis != null) jedis.close();
        }
    }

    // redis不支持script。。
    @Deprecated
    public boolean releaseOld(String lockKey, String requestId) {
        Jedis jedis = null;
        try {
            jedis = shardedJedisPool.getResource().getShard(lockKey);
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] " +
                    "then return redis.call('del', KEYS[1]) " +
                    "else return 0 end";
            Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
            if (RELEASE_SUCCESS.equals(result)) {
                return true;
            }
            return false;
        } catch (Exception e) {
            logger.error("exception in release: ", e);
            return false;
        } finally {
            if (jedis != null) jedis.close();
        }
    }

}
